﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;

namespace CoderJoe
{
    static class Program
    {
        private const int SleepTime = 10000;

        static void Main()
        {
            Console.WriteLine("--------------------------");
            Console.WriteLine("ReleaseWorkingLikeDebug");
            ReleaseWorkingLikeDebug();
            Console.WriteLine("--------------------------");

            Console.WriteLine("--------------------------");
            Console.WriteLine("SameResultInReleaseAndDebug");
            SameResultInReleaseAndDebug();
            Console.WriteLine("--------------------------");

            Console.WriteLine("--------------------------");
            Console.WriteLine("WorksInDebugSometimesInReleaseMode");
            WorksInDebugSometimesInReleaseMode();
            Console.WriteLine("--------------------------");
        }

        static void WorksInDebugSometimesInReleaseMode()
        {
            RootDisposable root = new RootDisposable();

            using (ChildDisposable child = root.GetMyChild())
            {
                // root CAN be disposed here, which triggers
                // child to be disposed ;)

                // Force Failure :)
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Thread.Sleep(SleepTime);

                try
                {
                    child.DoWork();
                }
                catch (ObjectDisposedException ex)
                {
                    Console.Error.WriteLine();
                    Console.Error.WriteLine("WTF");
                    Console.Error.WriteLine();
                    Console.Error.WriteLine(ex.ToString());
                }
            }
        }

        static void ReleaseWorkingLikeDebug()
        {
            RootDisposable root = new RootDisposable();

            using (ChildDisposable child = root.GetMyChild())
            {
                // the GC.KeepAlive(root) keeps root alive till
                // the end of the method.  Debug builds add this
                // automatically to prevent variables from going
                // out of scope in the watch window.

                // Won't Fail :)
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Thread.Sleep(SleepTime);

                try
                {
                    child.DoWork();
                }
                catch (ObjectDisposedException)
                {
                    Console.Error.WriteLine("WTF");
                }
            }

            GC.KeepAlive(root);
        }

        static void SameResultInReleaseAndDebug()
        {
            // the using blocks are transformed into
            // try { } finally{ root.Dispose(); child.Dispose() }
            // keeping root alive for the scope of the using ;)
            using (RootDisposable root = new RootDisposable())
            using (ChildDisposable child = root.GetMyChild())
            {
                // Won't Fail :)
                GC.Collect();
                GC.WaitForPendingFinalizers();
                GC.Collect();
                Thread.Sleep(SleepTime);

                try
                {
                    child.DoWork();
                }
                catch (ObjectDisposedException)
                {
                    Console.Error.WriteLine("WTF");
                }
            }
        }
    }
}
